package com.mostka.serializer.java;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;


public class Serializer implements SerializerInt {
	@SuppressWarnings("unused")
	private static final byte NONE		=0x00;
	private static final byte SHORT		=0x01;
	private static final byte SHORT_ARR	=0x02;
	private static final byte INT		=0x03;
	private static final byte INT_ARR	=0x04;
	private static final byte LONG		=0x05;
	private static final byte LONG_ARR	=0x06;
	private static final byte FLOAT		=0x07;
	private static final byte FLOAT_ARR	=0x08;
	private static final byte DOUBLE	=0x09;
	private static final byte DOUBLE_ARR=0x10;
	private static final byte BOOLEAN	=0x11;
	private static final byte BOOLEAN_ARR=0x12;
	private static final byte CHAR		=0x13;
	private static final byte CHAR_ARR	=0x14;
	private static final byte STRING	=0x15;
	private static final byte STRING_ARR=0x16;
	private static final byte BYTE_ARR	=0x17;

    @SuppressWarnings("serial")
	public class BadPrimitiveTypeException extends Exception{
		public BadPrimitiveTypeException(byte b1,byte b2) {
			super(b1+"!="+b2);
		}

        public BadPrimitiveTypeException() {
        }
    }
	
	private ArrayList<Byte> writeBuffer;
	private byte[] readBuffer;
	private int readPosition;
	
	public Serializer(){
		writeBuffer=new ArrayList<Byte>();
	}
	public Serializer(String buffer){
		readPosition=0;
		try {
			readBuffer=buffer.getBytes("ISO-8859-1");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeByte(byte[])
	 */
	public void writeByte(byte[] bs){
		writeByte(bs,true);
	}
	
	private void writeByte(byte[] bs ,boolean makker){
		if (makker){
			writeByte(BYTE_ARR);
			if (bs==null){
				writeValue(-1,false);
				return;
			}
			writeValue(bs.length,false);
		}
		for(byte b :bs){
			writeBuffer.add(b);
		}
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeByte(byte)
	 */
	public void writeByte(byte b){
		writeBuffer.add(b);
	}
	
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(byte)
	 */
	public void writeValue(byte b){
		writeByte(b);
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(byte[])
	 */
	public void writeValue(byte[] b){
		writeByte(b);
	}

	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(short)
	 */
	public void writeValue(short value){
		writeValue(value,true);
	}
	private void writeValue(short value,boolean makker){
		if (makker)writeByte(SHORT);
		writeByte(new byte[] {
			(byte)(value >>> 8),
		(byte)value},false);
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(short[])
	 */
	public void writeValue(short[] value) {
		writeByte(SHORT_ARR);
		if (value==null){
			writeValue(-1,false);
			return;
		}
		writeValue(value.length,false);
		for(short v : value){
			writeValue(v,false);
		}
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(int)
	 */
	public void writeValue(int value){
		writeValue(value,true);
	}
	private void writeValue(int value,boolean makker){
		if (makker)writeByte(INT);
		writeByte(new byte[] { 
                (byte) ((value << 24) >>> 24),
                (byte) ((value << 16) >>> 24),
				(byte) ((value << 8) >>> 24),
				(byte) (value >>> 24)
		},false);
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(int[])
	 */
	public void writeValue(int[] value) {
		writeByte(INT_ARR);
		if (value==null){
			writeValue(-1,false);
			return;
		}
		writeValue(value.length,false);
		for(int v : value){
			writeValue(v,false);
		}
	}

	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(long)
	 */
	public void writeValue(long value){
		writeValue(value,true);
	}
	private void writeValue(long value,boolean makker){
		if (makker)writeByte(LONG);
		writeByte(new byte [] {
            (byte) ((value << 56) >>> 56),
            (byte) ((value << 48) >>> 56),
            (byte) ((value << 40) >>> 56),
            (byte) ((value << 32) >>> 56),
            (byte) ((value << 24) >>> 56),
            (byte) ((value << 16) >>> 56),
			(byte) ((value << 8) >>> 56),
			(byte) (value >>> 56)}
		,false);
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(long[])
	 */
	public void writeValue(long[] value) {
		writeByte(LONG_ARR);
		if (value==null){
			writeValue(-1,false);
			return;
		}
		writeValue(value.length,false);
		for(long v : value){
			writeValue(v,false);
		}
	}
	
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(float, boolean)
	 */
	public void writeValue(float f,boolean makker){
		if (makker)writeByte(FLOAT);
		writeValue(Float.floatToIntBits(f),false);
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(float[])
	 */
	public void writeValue(float[] value) {
		writeByte(FLOAT_ARR);
		if (value==null){
			writeValue(-1,false);
			return;
		}
		writeValue(value.length,false);
		for(float v : value){
			writeValue(v,false);
		}
	}

	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(double)
	 */
	public void writeValue(double value){
		writeValue(value,true);
	}
	private void writeValue(double value,boolean makker){
		if (makker)writeByte(DOUBLE);
		writeValue(Double.doubleToLongBits(value),false);
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(double[])
	 */
	public void writeValue(double[] value) {
		writeByte(DOUBLE_ARR);
		if (value==null){
			writeValue(-1,false);
			return;
		}
		writeValue(value.length,false);
		for(double v : value){
			writeValue(v,false);
		}
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(boolean)
	 */
	public void writeValue(boolean value){
		writeValue(value,true);
	}
	private void writeValue(boolean value,boolean makker){
		if (makker)writeByte(BOOLEAN);
		writeByte((byte)(value?1:0));
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(boolean[])
	 */
	public void writeValue(boolean[] value) {
		writeByte(BOOLEAN_ARR);
		if (value==null){
			writeValue(-1,false);
			return;
		}
		writeValue(value.length,false);
		for(boolean v : value){
			writeValue(v,false);
		}
	}
	
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(char, boolean)
	 */
	public void writeValue(char value,boolean makker){
		if (makker)writeByte(CHAR);
		writeByte(new byte[]{
			(byte) (value >> 8),
		(byte) value}
		, makker);
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(char[])
	 */
	public void writeValue(char[] value) {
		writeByte(CHAR_ARR);
		if (value==null){
			writeValue(-1,false);
			return;
		}
		writeValue(value.length,false);
		for(char v : value){
			writeValue(v,false);
		}
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(java.lang.String)
	 */
	public void writeValue(String value){
		 writeValue(value,true);
	}
	private void writeValue(String value,boolean makker){
		if (makker)writeByte(STRING);
		writeValue(value.length(),false);
		writeByte(value.getBytes(), false);
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#writeValue(java.lang.String[])
	 */
	public void writeValue(String[] value) {
		writeByte(STRING_ARR);
		if (value==null){
			writeValue(-1,false);
			return;
		}
		writeValue(value.length,false);
		for(String v : value){
			writeValue(v,false);
		}
	}

	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#getBuffer()
	 */
	public String getBuffer(){
		try {
			return new String(getByteBuffer(),"ISO-8859-1");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return "";
		}
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#getByteBuffer()
	 */
	public byte[] getByteBuffer(){
		int s = writeBuffer.size();
		byte[] bytes = new byte[s];
		for (int i=0;i<s;i++){
			bytes[i]=writeBuffer.get(i);
		}
		return bytes;
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readByte()
	 */
	public byte readByte(){
		return readBuffer[readPosition++];
	}
	
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#checkType(byte)
	 */
	public void checkType(byte type) throws BadPrimitiveTypeException{
		byte b = readByte();
		if (b!=type){
			throw new BadPrimitiveTypeException(b,type);
		}
	}
	
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readByteArr()
	 */
	public byte[] readByteArr() throws BadPrimitiveTypeException{
		checkType(BYTE_ARR);
		int len = readInteger(false);
		if (len==-1){
			return null;
		}
		byte[] bytes = new byte[len];
		for(int i=0;i<len;i++){
			bytes[i]=readByte();
		}
		return bytes;
	}
	
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readShort()
	 */
	public short readShort() throws BadPrimitiveTypeException{
		checkType(SHORT);
		return (short)
		((readByte()<<8) +
		(readByte()));
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readShortArr()
	 */
	public short[] readShortArr() throws BadPrimitiveTypeException{
		checkType(SHORT_ARR);
		int len = readInteger(false);
		if (len==-1){
			return null;
		}
		short[] ret = new short[len];
		for(int i=0;i<len;i++){
			ret[i]=(short)
			((readByte()<<8) +
			(readByte()));
		}
		return ret;
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readInteger()
	 */
	public int readInteger() throws BadPrimitiveTypeException{
		return readInteger(true);
	}
	private int readInteger(boolean maker) throws BadPrimitiveTypeException{
		if (maker) checkType(INT);
		return readByte() & 0xFF | 
		(readByte() & 0xFF) << 8 |
        (readByte() & 0xFF) << 16 | 
        (readByte() & 0xFF) << 24;
	}

    public int[] readIntArr() throws BadPrimitiveTypeException{
        return readIntegerArr();
    }
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readIntegerArr()
	 */
	public int[] readIntegerArr() throws BadPrimitiveTypeException{
		checkType(INT_ARR);
		int len = readInteger(false);
		if (len==-1){
			return null;
		}
		int[] ret = new int[len];
		for(int i=0;i<len;i++){
			ret[i]=readByte() & 0xFF | 
					(readByte() & 0xFF) << 8 |
			        (readByte() & 0xFF) << 16 | 
			        (readByte() & 0xFF) << 24;
		}
		return ret;
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readLong()
	 */
	public long readLong() throws BadPrimitiveTypeException{
		checkType(LONG);
		return (
				readByte() & 0xFFl | 
				(readByte() & 0xFFl) << 8 |
		        (readByte() & 0xFFl) << 16 | 
		        (readByte() & 0xFFl) << 24 |
		        (readByte() & 0xFFl) << 32 |
		        (readByte() & 0xFFl) << 40 |
		        (readByte() & 0xFFl) << 48 |
		        (readByte() & 0xFFl) << 56);
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readLongArr()
	 */
	public long[] readLongArr() throws BadPrimitiveTypeException{
		checkType(LONG_ARR);
		int len = readInteger(false);
		if (len==-1){
			return null;
		}
		long[] ret = new long[len];
		for(int i=0;i<len;i++){
			ret[i]=(long)(
					readByte() & 0xFFl | 
					(readByte() & 0xFFl) << 8 |
			        (readByte() & 0xFFl) << 16 | 
			        (readByte() & 0xFFl) << 24 |
			        (readByte() & 0xFFl) << 32 |
			        (readByte() & 0xFFl) << 40 |
			        (readByte() & 0xFFl) << 48 |
			        (readByte() & 0xFFl) << 56);
		}
		return ret;
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readFloat()
	 */
	public float readFloat() throws BadPrimitiveTypeException{
		checkType(FLOAT);
		return Float.intBitsToFloat((int)(
		(readByte()<<16) +
		(readByte()<<8) +
		(readByte())));
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readFloatArr()
	 */
	public float[] readFloatArr() throws BadPrimitiveTypeException{
		checkType(FLOAT_ARR);
		int len = readInteger(false);
		if (len==-1){
			return null;
		}
		float[] ret = new float[len];
		for(int i=0;i<len;i++){
			ret[i]=Float.intBitsToFloat((int)(
			(readByte()<<16) +
			(readByte()<<8) +
			(readByte())));
		}
		return ret;
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readDouble()
	 */
	public double readDouble() throws BadPrimitiveTypeException{
		checkType(DOUBLE);
		return Double.longBitsToDouble((long)(
				readByte() & 0xFFl | 
				(readByte() & 0xFFl) << 8 |
		        (readByte() & 0xFFl) << 16 | 
		        (readByte() & 0xFFl) << 24 |
		        (readByte() & 0xFFl) << 32 |
		        (readByte() & 0xFFl) << 40 |
		        (readByte() & 0xFFl) << 48 |
		        (readByte() & 0xFFl) << 56));
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readDoubleArr()
	 */
	public double[] readDoubleArr() throws BadPrimitiveTypeException{
		checkType(DOUBLE_ARR);
		int len = readInteger(false);
		if (len==-1){
			return null;
		}
		double[] ret = new double[len];
		for(int i=0;i<len;i++){
			ret[i]=Double.longBitsToDouble((long)(
					readByte() & 0xFFl | 
					(readByte() & 0xFFl) << 8 |
			        (readByte() & 0xFFl) << 16 | 
			        (readByte() & 0xFFl) << 24 |
			        (readByte() & 0xFFl) << 32 |
			        (readByte() & 0xFFl) << 40 |
			        (readByte() & 0xFFl) << 48 |
			        (readByte() & 0xFFl) << 56));
		}
		return ret;
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readBoolean()
	 */
	public boolean readBoolean() throws BadPrimitiveTypeException{
		checkType(BOOLEAN);
		return readByte()==1;
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readBooleanArr()
	 */
	public boolean[] readBooleanArr() throws BadPrimitiveTypeException{
		checkType(BOOLEAN_ARR);
		int len = readInteger(false);
		if (len==-1){
			return null;
		}
		boolean[] ret = new boolean[len];
		for(int i=0;i<len;i++){
			ret[i]=readByte()==1;
		}
		return ret;
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readChar()
	 */
	public char readChar() throws BadPrimitiveTypeException{
		checkType(CHAR);
		return (char)(
		(readByte()<<8) +
		(readByte()));
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readCharArr()
	 */
	public char[] readCharArr() throws BadPrimitiveTypeException{
		checkType(CHAR_ARR);
		int len = readInteger(false);
		if (len==-1){
			return null;
		}
		char[] ret = new char[len];
		for(int i=0;i<len;i++){
			ret[i]=(char)(
			(readByte()<<8) +
			(readByte()));
		}
		return ret;
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readString()
	 */
	public String readString() throws BadPrimitiveTypeException{
		return readString(true);
	}
	private String readString(boolean checkMakker) throws BadPrimitiveTypeException{
		if (checkMakker) checkType(STRING);
		int len = readInteger(false);
		byte[] chars=new byte[len];
		for(int i=0;i<len;i++){
			chars[i]=readByte();
		}
		return new String(chars);
	}
	/* (non-Javadoc)
	 * @see com.mostka.serializer.java.SerializerInt#readStringArr()
	 */
	public String[] readStringArr() throws BadPrimitiveTypeException{
		checkType(STRING_ARR);
		int len = readInteger(false);
		if (len==-1){
			return null;
		}
		String[] ret = new String[len];
		for(int i=0;i<len;i++){
			ret[i]=readString(false);
		}
		return ret;
	}
}